Skip to content

fix(identity): actually wire the built-in verifier — a config key registers nothing - #235

Merged
rhCat merged 2 commits into
mainfrom
feat/wire-builtin-verifier
Jul 26, 2026
Merged

fix(identity): actually wire the built-in verifier — a config key registers nothing#235
rhCat merged 2 commits into
mainfrom
feat/wire-builtin-verifier

Conversation

@rhCat

@rhCat rhCat commented Jul 26, 2026

Copy link
Copy Markdown
Owner

#234 shipped the Ed25519 verifier and #232 the seam, and between them the feature was unusable: nothing ever called register_verifier. Setting auth_verifier="ed25519" made every claim fail closed — correct behaviour, and a silently dead feature.

Found by standing a real govd up from merged main and watching a valid assertion resolve to nobody. No unit test could have caught it — both halves were individually correct.

serve() now calls install_builtin_verifier(cfg) before the first claim. Only a name we ship is installed; an unknown name installs nothing and resolves nobody, which is the intended fail-closed behaviour for a typo — it must never fall back to the bearer-secret path.

Verifier code stays in the image by design: a verifier loaded from the mounted config would be ungoverned code executing inside the syscall boundary. The mount configures which verifier, never its body.

Verified against a real govd over real HTTP

alice's assertion  -> 200 allow, chain records principal=alice
undeclared key     -> 401
bearer secret      -> 401
no credential      -> 401

That principal=alice is the point of the whole exercise — a named identity in the audit trail, which a shared per-node token can never provide.

Tests

Two unit tests pin the helper. Because mutation showed that deleting the serve() call left every unit test green, there's also an integration test that starts a real govd on a free port and authenticates over HTTP — so the call site is covered, not just the function. Re-mutated after adding it: the suite now goes red.

🤖 Generated with Claude Code

rhCat and others added 2 commits July 25, 2026 23:06
…isters nothing

#234 shipped the Ed25519 verifier and #232 the seam, and between them the feature was unusable: nothing
ever called register_verifier. Setting auth_verifier="ed25519" made every claim fail closed — correct
behaviour, and a silently dead feature.

Found by standing a real govd up from merged main with auth_verifier="ed25519" and watching a valid
assertion resolve to nobody. No unit test could have caught it: both halves were individually correct.

serve() now calls install_builtin_verifier(cfg) before the first claim. Only a name we SHIP is installed; an
unknown name installs nothing and therefore resolves nobody, which is the intended fail-closed behaviour for
a typo — it must never fall back to the bearer-secret path. Verifier CODE stays in the image by design: a
verifier loaded from the mounted config would be ungoverned code executing inside the syscall boundary,
which is the one thing this system exists to prevent. The mount configures WHICH verifier, never its body.

Verified against a real govd over real HTTP (local, merged main, ed25519 scheme):

  alice's assertion   -> 200 allow, and the signed chain records principal=alice
  undeclared key      -> 401
  bearer secret       -> 401
  no credential       -> 401

That `principal=alice` is the point of the whole exercise: a named identity in the audit trail, which a
shared per-node token can never provide.

TESTS. Two unit tests pin the helper, and — because mutation showed deleting the serve() call left every
unit test GREEN — an integration test starts a real govd on a free port and authenticates over HTTP, so the
CALL SITE is covered, not just the function. Re-mutated after adding it: the suite now goes red.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI's enforcement-surface mutation ratchet caught this and it was right to:

  [FAIL] infra/govern/govd.py: score=0.95 floor=1.0  survivors=['==->!=@9986']
  MUTATION REGRESSION below floor: [('infra/govern/govd.py', 0.95, 1.0)]

govd.py was at a perfect 1.0 — every mutant killed — and the new `if name == "ed25519"` branch dropped it
to 0.95.

The branch was NOT untested: tests/test_ed25519_auth.py kills that mutant. But the ratchet drives each
enforcement-surface module against a DESIGNATED slice (infra/govern/selfmonitor_policy.json:
govd.py -> tests/test_govd.py), so a proof living anywhere else is invisible to it. Coverage for code in
govd.py belongs in govd.py's slice; that policy is the point, not an obstacle to route around — the
alternative (widening the slice) would dilute the ratchet for every module.

Verified by re-running the exact CI survivor locally: mutating `==` to `!=` now fails
tests/test_govd.py::test_install_builtin_verifier_registers_only_a_name_we_ship, and the suite is green
restored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rhCat
rhCat merged commit 51fa05a into main Jul 26, 2026
2 checks passed
@rhCat
rhCat deleted the feat/wire-builtin-verifier branch July 26, 2026 03:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant